package bma.common.langutil.ai.common;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import bma.common.langutil.ai.AIUtil;
import bma.common.langutil.ai.stack.AIStack;

/**
 * 互斥执行的处理器
 * 
 * @author guanzhong
 * @since 1.0 (imported from BMA.ESP Project)
 * 
 * @param <RESP_TYPE>
 */
public class AIMutex<RESP_TYPE> {

	protected List<AIRuntime<RESP_TYPE>> locks = new LinkedList<AIRuntime<RESP_TYPE>>();

	public void allFailure(Throwable t) {
		List<AIRuntime<RESP_TYPE>> tmp = null;
		synchronized (this) {
			tmp = new ArrayList<AIRuntime<RESP_TYPE>>(locks);
			locks.clear();
		}
		for (AIRuntime<RESP_TYPE> r : tmp) {
			AIUtil.safeFailure(r.getStack(), t);
		}
	}

	/**
	 * 互斥执行
	 * 
	 * @param fun
	 * @param stack
	 * @return
	 */
	public boolean execute(AIFunction<RESP_TYPE> fun, AIStack<RESP_TYPE> stack) {
		AIRuntime<RESP_TYPE> item = AIRuntime.create(fun, stack);
		item = push(item);
		if (item == null) {
			return false;
		}
		return _execute(item, true);
	}

	protected void executeOne(boolean apply) {
		AIRuntime<RESP_TYPE> item = next();
		if (item == null) {
			return;
		}
		_execute(item, apply);
	}

	protected boolean _execute(final AIRuntime<RESP_TYPE> item, boolean apply) {
		AIStack<RESP_TYPE> newStack = new AIStack<RESP_TYPE>() {

			private AtomicBoolean done = new AtomicBoolean(true);

			@Override
			public AIStack<?> getParent() {
				return item.getStack();
			}

			@Override
			public boolean success(RESP_TYPE result) {
				try {
					if (done.getAndSet(false)) {
						return item.getStack().success(result);
					}
					return true;
				} finally {
					executeOne(false);
				}
			}

			@Override
			public boolean failure(Throwable t) {
				try {
					if (done.getAndSet(false)) {
						return item.getStack().failure(t);
					}
					return true;
				} finally {
					executeOne(false);
				}
			}

		};

		try {
			if (apply) {
				return item.apply(newStack);
			} else {
				return item.run(newStack);
			}
		} catch (Throwable t) {
			return AIUtil.safeFailure(newStack, t);
		}
	}

	protected synchronized AIRuntime<RESP_TYPE> push(AIRuntime<RESP_TYPE> item) {
		List<AIRuntime<RESP_TYPE>> list = locks;
		list.add(item);
		if (list.size() > 1) {
			return null;
		}
		return item;
	}

	protected synchronized AIRuntime<RESP_TYPE> next() {
		List<AIRuntime<RESP_TYPE>> list = locks;
		if (!list.isEmpty()) {
			list.remove(0);
		}
		if (list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}
}
